package pres.seanlang.utility;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 *  数据流工具类
 *
 * @author SeanLang
 */
final public class StreamUtils {
    public static final int EOF = -1;

    public static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    protected StreamUtils() { throw new UnsupportedOperationException("can't instance..."); }

    public static InputStream openInputStream(String path) {
        InputStream stream = null;
        if(!StringUtils.isEmpty(path)) {
            try {
                stream = new FileInputStream(path);
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            }
        }

        return stream;
    }

    public static OutputStream openOutputStream(String path) {
        OutputStream stream = null;
        if(!StringUtils.isEmpty(path)) {
            try {
                stream = new FileOutputStream(path);
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            }
        }

        return stream;
    }

    /**
     *  写入buffer数据到OutputStream
     * @param stream
     * @param buffer
     * @param offset
     * @param length
     */
    public static void write(byte[] buffer, int offset, int length, OutputStream stream) {
        if( stream != null && buffer != null
                && (offset <= buffer.length && offset >= 0)
                && (length <= buffer.length && length > 0)
                && (offset + length) <= buffer.length ) {
            try {
                stream.write(buffer, offset, length);
                stream.flush();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     *  OutputStream的写入
     * @param data
     * @param stream
     */
    public static void write(String data, OutputStream stream) {
        if(!StringUtils.isEmpty(data) && stream != null) {
            byte[] buffer = data.getBytes();
            write(buffer, 0, buffer.length, stream);
        }
    }

    /**
     *  InputStream的读取
     * @param stream
     * @param buffer    读取缓存buffer
     * @param offset    缓存buffer的起始位置
     * @param length    buffer大小
     * @return  读取有效数据的size
     */
    public static int read(InputStream stream, byte[] buffer, int offset, int length) {
        if (stream != null && buffer != null
                && offset <= buffer.length && offset >= 0
                && length <= buffer.length && length >= 0
                && (offset + length) <= buffer.length ) {
            try {
                int remaining = length;
                while (remaining > 0) {
                    int location = length - remaining;
                    int count = stream.read(buffer, offset + location, remaining);
                    if(EOF == count) {
                        break;
                    }
                    remaining -= count;
                }

                return length - remaining;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return EOF;
    }

    /**
     *  关闭IO流
     * @param closeables
     */
    public static void close(Closeable... closeables) {
        if(closeables != null) {
            for (Closeable closeable : closeables) {
                if(closeable != null) {
                    try {
                        closeable.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     *  拷贝字节流, 耗时操作,当前读取数据长度为-1时停止.
     * @param input
     * @param output
     * @return
     */
    public static int copy(InputStream input, OutputStream output, boolean completeClose) {
        if(input != null && output != null) {
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int count = 0;
            int len = EOF;
            while((len = read(input, buffer, 0, buffer.length)) != EOF) {
                write(buffer, 0, len, output);
                count += len;
            }

            if(completeClose) {
                close(input, output);
            }
            return count;
        } else {
            return EOF;
        }
    }

    /**
     *  读取InputStream中所有的数据
     * @param input
     * @param completeClose 读取结束是否关闭InputStream
     * @return
     */
    public static byte[] readFully(InputStream input, boolean completeClose) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        int size = copy(input, output, completeClose);
        if(size != EOF) {
            return output.toByteArray();
        } else {
            return null;
        }
    }

    /**
     *  读取InputStream中的所有数据,且读取结束会关闭InputStream.
     * @param input
     * @return
     */
    public static byte[] readFully(InputStream input) {
        return readFully(input, true);
    }

    /**
     * 读取InputStream中的文本数据
     * @param input
     * @param completeClose 读取结束是否关闭InputStream
     * @return
     */
    public static String readFullyAsText(InputStream input, boolean completeClose) {
        byte[] bytes = readFully(input, completeClose);
        if(bytes != null) {
            return new String(bytes);
        } else {
            return "";
        }
    }

    /**
     *  读取InputStream中的所有数据,且读取结束会关闭InputStream.
     * @param input
     * @return
     */
    public static String readFullyAsText(InputStream input) {
        return readFullyAsText(input, true);
    }

    public static InputStream toInputStream(byte[] bytes, int offset, int len) {
        if(bytes == null || offset >= bytes.length || len >= offset) {
            return null;
        } else {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            output.write(bytes, offset, len);
            return new ByteArrayInputStream(output.toByteArray());
        }
    }

    public static InputStream toInputStream(byte[] bytes) {
        if(bytes == null) {
            return null;
        } else {
            return toInputStream(bytes, 0, bytes.length);
        }
    }

    public static InputStream toInputStream(String text) {
        if(text == null || text.length() == 0) {
            return null;
        } else {
            return toInputStream(text.getBytes());
        }
    }

    public static OutputStream getOutputStream(String path) {
        try {
            return new FileOutputStream(path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static InputStream getInputStream(String path) {
        try {
            return new FileInputStream(path);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static BufferedReader getBufferedReader(String path) {
        try {
            FileReader reader = new FileReader(path);
            return new BufferedReader(reader);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static BufferedWriter getBufferedWriter(String path) {
        try {
            FileWriter writer = new FileWriter(path);
            return new BufferedWriter(writer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
